# source E:/work/common_interface_sim/tcl/sim.tcl
# source "$env(HOME)/vimrc/tcl/sim.tcl"
# ::Sim::map_vivado2017_04_lib
#
# API: 
#       1. findIPSimFIles: 在compile.do文件搜索ip仿真需要的文件. 
#           NOTE: 设置ip名不能是默认ip名的一部分, 不然会导致选择文件时出现错误
#           NOTE: project_path需要指定到xx.sim, xx.scrs的上级目录
#               set project_path /home/wj/tmp/project_1
#               set project_name project_1
#               set ip_name mac_core
#               set ip_files [::Sim::findIPSimFIles $project_path $project_name $ip_name]
#
#       findIPNeedLib: 映射IP仿真需要的库，vsim时需要手动链接
#               set ip_need_libs [::Sim::findIPNeedLib $project_path $project_name $ip_name]
#               ::Sim::map_ip_lib $ip_need_libs
#
#       findFiles: 编译指定目录下所有verilog文件
#               foreach f [::Sim::findFiles ../hdl *.v] {
#                   vlog -incr $f
#               }
#
#       append linkLib $::Sim::linkLib
#       set ips [list clk_wiz_0 ila_0 adc_clk streamFIFO128x512 ]
#       foreach ip_name $ips {
#           set ip_need_libs [::Sim::findIPNeedLib $project_path $project_name $ip_name]
#           ::Sim::map_ip_lib $ip_need_libs
#           append linkLib [::Sim::link_ip_lib $ip_need_libs $linkLib]
#           set ip_files [::Sim::findIPSimFIles $project_path $project_name $ip_name]
#           foreach f $ip_files {
#               set extension [file extension $f]
#               if {[string equal $extension ".vhd"]} {
#                   vcom  $f
#               } else {
#                   vlog -incr $f
#               }
#           }
#       }
#
if {[namespace exists Sim] == 1} {
    namespace delete Sim
    puts "delete old namespace"
}

namespace eval Sim {
    variable vivado_version "" 
    variable linkLib " -L secureip \
            -L simprims_ver \
            -L unifast -L unifast_ver \
            -L unimacro -L unimacro_ver \
            -L unisim -L unisims_ver "

    variable VIVADO_2017_4_QUESTASIM_10_6C_SIM             {F:/crack/vivado2017.4_questasim_10.6c_lib}
    variable VIVADO_2018_3_QUESTASIM_10_6C_SIM             {F:/crack/vivado2018.3_questasim_10.6c_lib}

    if { [string equal $tcl_platform(os) "Linux"] == 1 } {
        variable VIVADO_2021_2_QUESTASIM_2021_2_1_SIM      {/home/wj/program/vivado2021.2_questasim_2021.2_1_lib}
        variable VIVADO_2018_3_QUESTASIM_2021_2_1_SIM      {/home/wj/program/vivado2018.3_questasim_2021.2_1_lib}
        # variable ISE14_7_QUESTASIM_2021_2_1_SIM            {/home/wj/F/crack/ise14.7_questasim_2021.2_1_lib}
        variable ISE14_7_QUESTASIM_2021_2_1_SIM            {/home/wj/program/ise14.7_questasim_2021.2_1_lib}
    } else {
        variable VIVADO_2018_3_QUESTASIM_2021_2_1_SIM      {F:/crack/vivado2018.3_questasim_2021.2_1_lib}
        variable ISE14_7_QUESTASIM_2021_2_1_SIM            {F:/crack/ise14.7_questasim_2021.2_1_lib}
    }

    proc printInfo {} {
        puts {::Sim::findIPSimFIles $project_path $project_name $ip_name}
    }

    # 仿真一个IP, 需要编译相应的库以及文件. 
    # 本函数从compile.do中找到需要自己编译的文件
    # 只找路径匹配IP名称的文件，其他文件大都是以库的形式存在，我们直接加载预编译库即可

    # ----------------findIPNeedLib函数从compile.do中找到需要加载的库文件
    proc findIPSimFIles {project_path project_name ip_name} {
        set sim_files {}
        set compile_do_path "${project_path}/${project_name}.ip_user_files/sim_scripts/${ip_name}/modelsim/compile.do"
        set fp [open $compile_do_path r]

        while { [gets $fp line] >= 0 } {

            if {[string match "vlib*" $line]} {
                continue
            }
            if {[string match "vmap*" $line]} {
                continue
            }
            if {[string match "vlog*" $line]} {
                continue
            }
            if {[string match "vcom*" $line]} {
                continue
            }
            # 如果文件路径和ip_name相匹配，那么这个文件就是仿真IP需要的文件
            # 其他的文件都是编译库所用，我们直接加载预编译库，不需要这些
            if { [string match *${ip_name}* $line] == 1 } {
                # 删除文件路径中双引号
                regsub -all {[\"|\\]} $line  "" l
                set l [string trim $l]

                # lappend sim_files [regsub {\.\.(/\.\.)+} $l $project_path]

                # 文件路径中..的个数
                set level_num [regexp -all {\.\.} $l]
                # compile.do 路径分割
                set split_num [llength [file split $compile_do_path]]

                # 1. 获得盘符
                # 2. ../.. 代表的文件路径
                set volume [lindex [file split $compile_do_path] 0]
                set path [join [ lrange [file split $compile_do_path] 1 [expr $split_num - $level_num -2]] {/}]
                lappend sim_files [regsub {\.\.(/\.\.)+} $l $volume$path]
            }
        }
        close $fp
        return $sim_files
    }

    # 从compile.do文件中搜索IP仿真所需要的库
    #
    # compile.do文件中会创建一些在预编译库中已经存在的库，这些我们可以直接跳过
    # 基本上除了xil_defaultlib库外，其他所有的库，预编译库中都有
    proc findIPNeedLib {project_path project_name ip_name} {
        set sim_libs {}
        set compile_do_path "${project_path}/${project_name}.ip_user_files/sim_scripts/${ip_name}/modelsim/compile.do"
        set fp [open $compile_do_path r]

        # 
        while { [gets $fp line] >= 0 } {
            if { [string match {vmap*} $line] == 1 } {
                if { [string match {*xil_defaultlib*} $line] == 0 } {
                    # puts $line
                    lappend sim_libs [file tail $line]
                }
            }
        }
        close $fp
        return $sim_libs
    }

    # search all IPs in the project
    # return a list containing ip
    # Principle: All IPs in vivado will generate related files 
    #            in the sim_scripts directory 
    proc findIPs {project_path project_name} {
        set IPs {}
        set p1 "${project_path}/${project_name}.ip_user_files/sim_scripts"

        foreach path [glob -directory $p1 *] {
            lappend IPs [file tail $path]
        }
        return $IPs
    }


    # findIPs $project_path $project_name

    # if using auto mode, it will auto find all IP simulation file in the project
    proc findProjectSimFiles {project_path project_name} {
        set simFiles {}
        # 找到所有的IP
        foreach ip [findIPs $project_path $project_name] {
            foreach f [findIPSimFIles $project_path $project_name $ip] {
                lappend simFiles $f
            }
        }
        return $simFiles
    }

    # 
    proc compileIP {fileList} {
        foreach f  $fileList {
            puts $f
            if {[file extension $f] == ".v"} {
                vlog -incr $f
            } elseif {[file extension $f] == ".sv"} {
                vlog -incr -sv $f
            } else {
                vcom -64 -93 $f
            }
        }
    }

    proc map_vivado_questasim_2021_2_1_lib { version } {
        if { [string equal $version "2021.2"] == 1 } {
            set Sim::vivado_version "2021.2"
            map_vivado2021_02_questasim_2021_2_1_lib
        } elseif { [string equal $version "2018.3"] == 1 } {
            set Sim::vivado_version "2018.3"
            map_vivado2018_03_questasim_2021_2_1_lib
        }
    }
    # 映射vivado仿真常用的库
    proc map_vivado2021_02_questasim_2021_2_1_lib {} {
        vmap secureip                   ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/secureip
        vmap simprims_ver               ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/simprims_ver
        vmap unifast                    ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/unifast
        vmap unifast_ver                ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/unifast_ver
        vmap unimacro                   ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/unimacro
        vmap unimacro_ver               ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/unimacro_ver
        vmap unisim                     ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/unisim
        vmap unisims_ver                ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/unisims_ver
        vmap fifo_generator_v13_2_6     ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/fifo_generator_v13_2_6
        vlog -incr ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/glbl.v;
    }
    proc map_vivado2018_03_questasim_2021_2_1_lib {} {
        vmap secureip                   ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/secureip
        vmap simprims_ver               ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/simprims_ver
        vmap unifast                    ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/unifast
        vmap unifast_ver                ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/unifast_ver
        vmap unimacro                   ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/unimacro
        vmap unimacro_ver               ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/unimacro_ver
        vmap unisim                     ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/unisim
        vmap unisims_ver                ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/unisims_ver
        vmap fifo_generator_v13_2_3     ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/fifo_generator_v13_2_3
        vlog -incr ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/glbl.v;
    }

    proc map_ise14_7_questasim_2021_2_1_lib {} {
        # vmap secureip                   ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/secureip
        # vmap simprims                   ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/simprims_ver
        # vmap simprims_ver               ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/simprims_ver
        # vmap unimacro                   ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/unimacro
        # vmap unimacro_ver               ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/unimacro_ver
        # vmap unisim                     ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/unisim
        # vmap unisims_ver                ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/unisims_ver
        # vmap xilinxcorelib              ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/xilinxcorelib
        # vmap xilinxcorelib_ver          ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/xilinxcorelib_ver
        # vlog -incr ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}/glbl.v;
        vmap ise_k7_libs ${Sim::ISE14_7_QUESTASIM_2021_2_1_SIM}
        # /home/wj/program/ise14.7_questasim_2021.2_1_lib
    }

    # 这里只是map, vsim的时候需要是用-L选项链接
    proc map_ip_lib {libs} {
        if { [string equal ${Sim::vivado_version} "2021.2"] == 1 } {
            foreach lib $libs {
                vmap $lib  ${Sim::VIVADO_2021_2_QUESTASIM_2021_2_1_SIM}/$lib
            }
        } else {
            foreach lib $libs {
                vmap $lib  ${Sim::VIVADO_2018_3_QUESTASIM_2021_2_1_SIM}/$lib
            }
        }
    }

    # 判断libs里的内容是否已经在origin_libs里了
    # 如果不再
    proc link_ip_lib {libs origin_libs} {
        set result ""
        foreach lib $libs {
            if {[string first $lib $origin_libs] != -1} {
                puts "origin_libs 包含 $lib"
            } else {
                append result " -L "
                append result $lib
            }
        }
        return $result
    }


    # findFiles
    # basedir - the directory to start looking in
    # pattern - A pattern, as defined by the glob command, that the files must match
    # example ::Sim::findFiles ../hdl *.v
    #
    # foreach f [::Sim::findFiles ../hdl *.v] {
    #     vlog -incr $f
    # }
    proc findFiles { basedir pattern } {

        # Fix the directory name, this ensures the directory name is in the
        # native format for the platform and contains a final directory seperator
        set basedir [string trimright [file join [file normalize $basedir] { }]]
        set fileList {}

        # Look in the current directory for matching files, -type {f r}
        # means ony readable normal files are looked at, -nocomplain stops
        # an error being thrown if the returned list is empty
        foreach fileName [glob -nocomplain -type {f r} -path $basedir $pattern] {
            lappend fileList $fileName
        }

        # Now look for any sub direcories in the current directory
        foreach dirName [glob -nocomplain -type {d  r} -path $basedir *] {
            # Recusively call the routine on the sub directory and append any
            # new files to the results
            set subDirList [findFiles $dirName $pattern]
            if { [llength $subDirList] > 0 } {
                foreach subDirFile $subDirList {
                    lappend fileList $subDirFile
                }
            }
        }
        return $fileList
    }

    # 找到*.xpr文件，如果此文件只有一个, 则输出project_path和project_name
    # project_path: xpr 文件所在的目录
    # project_name: xx.srcs, xx.sim前的xx

    # path: 搜索起始目录
    # name: 工程名, 例如 main.xpr, 则输入main 
    #
    # set result [findVivadoProjectInfo /home/wj/study/hdl_example/ datamover]
    # puts [lindex $result 0]
    # puts [lindex $result 1]
    proc findVivadoProjectInfo { path name } {
        set files [::Sim::findFiles $path *.xpr]
        set project_path ''
        set project_name ''

        if { [llength $files] == 1 } {
            set project_path [ file dirname $files ]
            set pattern {/([^/]+)\.xpr}

            if {[regexp $pattern $files match subString]} {
                set project_name $subString
            }
        } else {

            foreach f $files {
                if {[string match "*$name.xpr*" $f]} {
                    set project_path [ file dirname $f ]
                    set pattern {/([^/]+)\.xpr}

                    if {[regexp $pattern $f match subString]} {
                        set project_name $subString
                    }
                }
            }
        }

        return [list $project_path $project_name]
    }
}
